﻿using Lazy.Captcha.Core.Interface;
using WeDonekRpc.CacheClient.Interface;
using WeDonekRpc.Client.Attr;
using WeDonekRpc.Helper;

namespace Lazy.Captcha.Core.Limit
{
    [ClassLifetimeAttr(ClassLifetimeType.SingleInstance)]
    internal class VerCodeRateLimit : IVerCodeRateLimit
    {
        private readonly ICacheController _Cache;
        private readonly IRateLimitConfig _Config;
        private const string _CacheKey = "VerLimit_";

        public VerCodeRateLimit ( ICacheController cache, IRateLimitConfig config )
        {
            this._Cache = cache;
            this._Config = config;
        }
        public void IsAllowed ( string key )
        {
            this._IsAllowed(key, 0);
        }
        private void _IsAllowed ( string key, int retry )
        {
            if ( !this._Config.Enabled )
            {
                return;
            }
            string t = _CacheKey + key;
            if ( !this._Cache.TryGet(t, out VerCodeLimit limit) )
            {
                limit = new VerCodeLimit
                {
                    Key = key,
                    Num = 1,
                    Expire = DateTime.Now.Add(this._Config.WindowSeconds),
                };
                if ( !this._Cache.Add(t, limit, this._Config.WindowSeconds) )
                {
                    if ( retry >= 3 )
                    {
                        throw new ErrorException("hr.ver.code.limit.add.fail");
                    }
                    this._IsAllowed(key, retry + 1);
                    return;
                }
            }
            else
            {
                if ( limit.Expire <= DateTime.Now )
                {
                    limit.Num = 1;
                }
                else
                {
                    limit.Num += 1;
                }
                _ = this._Cache.Set(key, limit, this._Config.WindowSeconds);
            }
            if ( limit.Num >= this._Config.MaxRequests )
            {
                throw new ErrorException("hr.login.ver.code.lmit", "time:" + (int)( limit.Expire - DateTime.Now ).TotalSeconds);
            }
        }
    }
}
